package com.atguigu.gulimail.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.constant.OrderStatusEnum;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.OrderTo;
import com.atguigu.common.to.SkuStock;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberResponseVo;
import com.atguigu.gulimail.order.constant.OrderConstant;
import com.atguigu.gulimail.order.entity.OrderItemEntity;
import com.atguigu.gulimail.order.feign.CartFeignService;
import com.atguigu.gulimail.order.feign.MemberFeignService;
import com.atguigu.gulimail.order.feign.ProductFeignService;
import com.atguigu.gulimail.order.feign.WareFeignService;
import com.atguigu.gulimail.order.interceptor.MyOrderInterceptor;
import com.atguigu.gulimail.order.service.OrderItemService;
import com.atguigu.gulimail.order.to.OrderCreateTo;
import com.atguigu.gulimail.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimail.order.dao.OrderDao;
import com.atguigu.gulimail.order.entity.OrderEntity;
import com.atguigu.gulimail.order.service.OrderService;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
        ThreadLocal<OrderSubmitVo> threadLocal=new ThreadLocal<>();
     @Autowired
    OrderItemService orderItemService;
    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberResponseVo member = MyOrderInterceptor.threadLocal.get();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //1、获取用户所有收货地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(member.getId());
            orderConfirmVo.setAddressVoList(address);
        }, executor);


        CompletableFuture<Void> itemTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //2、获取所有选中的购物项目
            List<OrderItemVo> cartItem = cartFeignService.getCartItem();
            orderConfirmVo.setItemVos(cartItem);
        }, executor).thenRunAsync(()->{
            // 这里也是异步查询。 但是这里查询库存并不需要判断是否登录。 那么在拦截器就要配置。
            List<OrderItemVo> itemVos = orderConfirmVo.getItemVos();
            List<Long> skuIds = itemVos.stream().map(item -> {
                return item.getSkuId();
            }).collect(Collectors.toList());
            //调用库存系统查询。
            R r = wareFeignService.hasStock(skuIds);
            List<SkuStock> stocks = r.getData(new TypeReference<List<SkuStock>>() {
            });
            Map<Long, Boolean> collect = stocks.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::isHasStock));
            orderConfirmVo.setStocks(collect);

        },executor);



        //3、优惠卷
        Integer integration = member.getIntegration();
        orderConfirmVo.setIntergration(integration);

        //4、其他信息自动计算

        //5、TODO 防重令牌。 幂等性
        String tocken = UUID.randomUUID().toString().replace("-", "");
        //redis里面存放令牌的规则。 user:order:id,tocken
        stringRedisTemplate.opsForValue().set(OrderConstant.USER_REDIS_ORDER_PREFIX+member.getId(),tocken,30, TimeUnit.MINUTES);

        orderConfirmVo.setOrderTocken(tocken);

        CompletableFuture.allOf(addressTask,itemTask).get();

        return orderConfirmVo;
    }
    //TODO 确认订单的时候，往vo里面放入令牌。

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public OrderSubmitResponseVo submitOrder(OrderSubmitVo vo) {
        threadLocal.set(vo);
        OrderSubmitResponseVo responseVo=new OrderSubmitResponseVo();
        responseVo.setCode(0);
        //只要登录了  就可以获取用户vo
        MemberResponseVo memberResponseVo = MyOrderInterceptor.threadLocal.get();
        String orderTocken = vo.getOrderTocken();
        //redis 比对令牌和删除令牌必须是原子性。原子性就必须使用lua脚本
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //执行lua脚本   注意这里传的参数是key.
        Long execute = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(OrderConstant.USER_REDIS_ORDER_PREFIX + memberResponseVo.getId()), orderTocken);
        if (execute==1L){
            responseVo.setCode(0);
            //删除成功
            //创建订单
            OrderCreateTo order = createOrder();
            BigDecimal payAmount = order.getOrder().getPayAmount();
            BigDecimal payPrice = vo.getPayPrice();
           if( Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
                   // 金额对比成功。
                   //TODO 3、保存订单
                saveOrder(order);
                responseVo.setOrder(order.getOrder());

               WareLockVo wareLockVo=new WareLockVo();
               wareLockVo.setOrderSn(order.getOrder().getOrderSn());
               List<OrderItemVo> orderItemVos = order.getOrderItems().stream().map(item -> {
                   OrderItemVo orderItemVo = new OrderItemVo();
                   orderItemVo.setSkuId(item.getSkuId());
                   orderItemVo.setCount(item.getSkuQuantity());
                   orderItemVo.setTitle(item.getSpuName());
                   return orderItemVo;
               }).collect(Collectors.toList());
               //TODO 4、锁定库存。
               // 需要skuId skuName  count  只需要这三个。所以重新封装以下。
               wareLockVo.setItemVos(orderItemVos);
               R r = wareFeignService.lockStock(wareLockVo);
               if (r.getCode()==0){
                   //锁定库存成功。

                   // TODO 5、模拟远程扣积分 业务失败。 在没有使用seata分布式锁的情况下。 锁定库存的业务不会回滚
                        //TODO 而我在库存锁定服务中 使用了消息机制。 可以使用柔性事务
                   //int i=10/0;
                // 订单创建成功。 向rabbitMQ发送消息  给延时队列发送消息
                   System.out.println("订单创建成功，延时队列发送消息。");
                   rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order",order.getOrder());

                   return responseVo;
               }else{
                   responseVo.setCode(3);//锁库存失败。
                   return responseVo;
                    //throw  new NoStockException();
               }
           }else{
               responseVo.setCode(2); //金额对失败
               return responseVo;
           }
        }else{
            //删除失败
            responseVo.setCode(1);
        }

        return responseVo;
    }

    @Override
    public OrderEntity getOrderWithOrderSn(String orderSn) {

        OrderEntity orderEntity = this.baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        if (orderEntity!=null){
            return orderEntity;
        }else{
            return null;
        }
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) throws ExecutionException, InterruptedException {
            //如果30分钟过去了。订单状态还是新建状态，则表示用户未付款。
            if (orderEntity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()) {
                OrderEntity update = new OrderEntity();
                update.setId(orderEntity.getId());
                update.setStatus(OrderStatusEnum.CANCLED.getCode());
                //更新订单状态。 将其更新为取消状态
                this.updateById(update);
                    //假设这个更新状态卡主了。
            }
        OrderTo to=new OrderTo();
        BeanUtils.copyProperties(orderEntity,to);
            //如果上面卡主了 就是订单状态不会更新为以取消状态
            //这里应该给stock.release.stock.queue 这个队列发送一个消息。
            rabbitTemplate.convertAndSend("order-event-exchange",
                    "order.release.other",
                    to);
        System.out.println("直接给库存释放队列发送消息。");
    }

    @Override
    public PayVo getPayVoBySn(String orderSn) {
        PayVo payVo=new PayVo();
        /**
         *    private String out_trade_no; // 商户订单号 必填
         *     private String subject; // 订单名称 必填
         *     private String total_amount;  // 付款金额 必填
         *     private String body; // 商品描述 可空
         */

        payVo.setOut_trade_no(orderSn);
        payVo.setSubject(UUID.randomUUID().toString().substring(0,5)+"随机名称");
        OrderEntity orderWithOrderSn = this.getOrderWithOrderSn(orderSn);
        //保留两位小数，并向上取值
        BigDecimal bigDecimal = orderWithOrderSn.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        String s = bigDecimal.toString();
        payVo.setTotal_amount(s);
        payVo.setBody("哈哈哈哈 买的好");
        return payVo;

    }

    /**
     * 获取当前用户的所用订单和所有订单项
     * @param params
     * @return
     */
    @Override
    public PageUtils getOrderList(Map<String, Object> params) {
        MemberResponseVo memberResponseVo = MyOrderInterceptor.threadLocal.get();

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", memberResponseVo.getId())
        );

        List<OrderEntity> records = page.getRecords();
        List<OrderEntity> orderSn = records.stream().map(item -> {
            List<OrderItemEntity> order_sn = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", item.getOrderSn()));

            item.setOrderItemEntity(order_sn);
            return item;
        }).collect(Collectors.toList());
        page.setRecords(orderSn);
        return new PageUtils(page);

    }

    /**
     * 保存订单和订单里的订单项
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        //1、首先保存订单。
        save(order.getOrder());
        //2、保存订单项
        List<OrderItemEntity> orderItems = order.getOrderItems();
        orderItemService.saveBatch(orderItems);
    }

    /**
     * 创建订单
     * @return
     */
    private OrderCreateTo createOrder(){

        OrderCreateTo orderCreateTo=new OrderCreateTo();
        //1、生成一个订单号。
        String orderSn = IdWorker.getTimeId();
        //2、创建订单
        OrderEntity orderEntity = buildOrder(orderSn);

        // 3、获取到所有的订单项信息
        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn);
        orderCreateTo.setOrderItems(orderItemEntities);
        //4、计算价格 积分相关的信息
        computerPrice(orderEntity,orderItemEntities);

        orderCreateTo.setOrder(orderEntity);

        return orderCreateTo;
    }

    private void computerPrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {

        BigDecimal total=new BigDecimal("0");

        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal intergration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");

        //积分、成长值
        Integer integrationTotal = 0;
        Integer growthTotal = 0;
        for (OrderItemEntity orderItemEntity : orderItemEntities) {
            //优惠价格信息
            coupon = coupon.add(orderEntity.getCouponAmount());
            promotion = promotion.add(orderEntity.getPromotionAmount());
            intergration = intergration.add(orderEntity.getIntegrationAmount());
            // 订单的总额。
            total=total.add(orderItemEntity.getRealAmount());

            //积分信息和成长值信息
            integrationTotal += orderItemEntity.getGiftIntegration();
            growthTotal += orderItemEntity.getGiftGrowth();

        }
        //订单总额
        orderEntity.setTotalAmount(total);
        //应付总额
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setCouponAmount(coupon);
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(intergration);

        //设置积分成长值信息
        orderEntity.setIntegration(integrationTotal);
        orderEntity.setGrowth(growthTotal);

        //设置删除状态(0-未删除，1-已删除)
        orderEntity.setDeleteStatus(0);


    }

    private OrderEntity buildOrder(String orderSn) {
        MemberResponseVo memberResponseVo = MyOrderInterceptor.threadLocal.get();

        OrderEntity orderEntity=new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        //1、设置会员Id
        orderEntity.setMemberId(memberResponseVo.getId());
        //2、获取物流信息
        OrderSubmitVo orderSubmitVo = threadLocal.get();
        R r = wareFeignService.getFareAddress(orderSubmitVo.getAddressId());
        FareAddressVo data = r.getData(new TypeReference<FareAddressVo>() {
        });
        orderEntity.setReceiverCity(data.getMemberAddressVo().getCity());
        orderEntity.setReceiverDetailAddress(data.getMemberAddressVo().getDetailAddress());
        orderEntity.setReceiverProvince(data.getMemberAddressVo().getProvince());
        orderEntity.setReceiverPhone(data.getMemberAddressVo().getPhone());
        orderEntity.setReceiverName(data.getMemberAddressVo().getName());
        orderEntity.setReceiverRegion(data.getMemberAddressVo().getRegion());
        //设置运费
        orderEntity.setFreightAmount(data.getFare());
        //设置订单创建时间，以及订单状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setModifyTime(new Date());

        orderEntity.setCouponAmount(new BigDecimal("0"));
        orderEntity.setIntegrationAmount(new BigDecimal("0"));
        orderEntity.setPromotionAmount(new BigDecimal("0"));


        return orderEntity;
    }


    /**
     * 构建每一个订单项数据
     * @param itemVo
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo itemVo,String orderSn) {
        OrderItemEntity orderItemEntity=new OrderItemEntity();
        //0、订单号
        orderItemEntity.setOrderSn(orderSn);
        //1、spu相关信息
        R r = productFeignService.getSpuInfo(itemVo.getSkuId());
        SpuInfoVo spuInfoVo = r.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfoVo.getId());
        orderItemEntity.setSpuName(spuInfoVo.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
        orderItemEntity.setCategoryId(spuInfoVo.getCatalogId());

        //2、sku相关信息
        orderItemEntity.setSkuId(itemVo.getSkuId());
        orderItemEntity.setSkuName(itemVo.getTitle());
        orderItemEntity.setSkuPic(itemVo.getImage());
        orderItemEntity.setSkuPrice(itemVo.getPrice());
        orderItemEntity.setSkuQuantity(itemVo.getCount());
        List<String> skuAttr = itemVo.getSkuAttr();
        String arrts = StringUtils.collectionToDelimitedString(skuAttr, ";");
        orderItemEntity.setSkuAttrsVals(arrts);
        //3、成长。积分信息
        orderItemEntity.setGiftGrowth(itemVo.getPrice().multiply(new BigDecimal(itemVo.getCount().toString())).intValue());
        orderItemEntity.setGiftIntegration(itemVo.getPrice().multiply(new BigDecimal(itemVo.getCount().toString())).intValue());
        //4、优惠信息。不做
        //5、促销分解金额。
        orderItemEntity.setPromotionAmount(new BigDecimal("0"));
        orderItemEntity.setCouponAmount(new BigDecimal("0"));
        orderItemEntity.setIntegrationAmount(new BigDecimal("0"));
        //6、当前商品的真实金额等于skuPrice*数量-优惠金额
        BigDecimal skutotal = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity()));
        BigDecimal realAmount = skutotal.subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(realAmount);


        return orderItemEntity;
    }

    /**
     * 构建所有的订单项信息
     * @return
     */
    public  List<OrderItemEntity> buildOrderItems(String  orderSn){
        List<OrderItemVo> cartItem = cartFeignService.getCartItem();
        List<OrderItemEntity> collect = cartItem.stream().map(itemVo -> {
            OrderItemEntity orderItemEntity = buildOrderItem(itemVo,orderSn);

            return orderItemEntity;
        }).collect(Collectors.toList());
        return collect;
    }

}